package com.fuyun.rpc.server.protocol;

import com.fuyun.rpc.common.domain.RpcRequest;
import com.fuyun.rpc.common.domain.RpcResponse;
import com.fuyun.rpc.common.security.Authenticator;
import com.fuyun.rpc.common.security.AuthenticatorFactory;
import com.fuyun.rpc.common.serializer.Serializer;
import com.fuyun.rpc.common.trace.TraceContext;
import com.fuyun.rpc.common.trace.TraceContextManager;
import com.fuyun.rpc.common.utils.MockDataUtil;
import com.fuyun.rpc.server.config.RpcConfig;
import com.fuyun.rpc.server.registry.LocalRegistry;
import io.vertx.core.buffer.Buffer;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Method;

/**
 * @author: zrq1351
 * @date: 2025/2/20
 */
@Slf4j
public abstract class AbstractServerHandler {

	protected final Serializer serializer;
	protected final RpcConfig rpcConfig;

	public AbstractServerHandler(Serializer serializer, RpcConfig rpcConfig) {
		this.serializer = serializer;
		this.rpcConfig = rpcConfig;
	}

	/**
	 * 处理接收到的数据并生成响应
	 *
	 * @param buffer     包含请求数据的缓冲区
	 * @param connection 服务器连接对象，用于发送响应
	 */
	protected void processData(Buffer buffer, ServerConnection connection) {
		RpcRequest obj;
		try {
			obj = serializer.deserialize(buffer.getBytes(), RpcRequest.class);
		} catch (Exception e) {
			log.error("无法反序列化 RpcRequest", e);
			sendErrorResponse(connection, "Failed to deserialize RpcRequest");
			return;
		}

		// 设置链路追踪上下文
		TraceContext traceContext = obj.getTraceContext();
		if (traceContext != null) {
			// 创建子追踪上下文
			traceContext = TraceContextManager.createChildContext(traceContext, obj.getServiceName(),
			                                                      obj.getMethodName());
		} else {
			// 创建新的追踪上下文
			traceContext = TraceContextManager.createNewContext(obj.getServiceName(), obj.getMethodName());
		}

		// 检查是否需要认证
		if (rpcConfig.isEnableAuth()) {
			if (!authenticateClient(obj)) {
				log.warn("客户端认证失败");
				sendErrorResponse(connection, "Authentication failed");

				// 清除追踪上下文
				TraceContextManager.clearContext();
				return;
			}
		}

		RpcResponse rpcResponse = new RpcResponse();
		rpcResponse.setTraceContext(traceContext);

		try {
			Class<?> implClass = LocalRegistry.get(obj.getServiceName());
			Method method = implClass.getMethod(obj.getMethodName(), obj.getParameterTypes());
			Object result;
			if (rpcConfig.getMock()) {
				result = MockDataUtil.generate(method.getReturnType());
			} else {
				result = method.invoke(implClass.getDeclaredConstructor().newInstance(), obj.getArgs());
			}

			rpcResponse.setData(result);
			rpcResponse.setDataType(method.getReturnType());
			rpcResponse.setMessage("success");
		} catch (Exception e) {
			log.error("调用 method 失败", e);
			rpcResponse.setMessage(e.getMessage());
			rpcResponse.setException(e);
		} finally {
			// 结束链路追踪
			traceContext.setEndTime(System.currentTimeMillis());
			log.info("RPC调用链路追踪: {}", traceContext);
			TraceContextManager.clearContext();
		}

		// 返回响应
		try {
			Buffer responseBuffer = Buffer.buffer(serializer.serialize(rpcResponse));
			sendResponse(connection, responseBuffer);
			log.info("响应正文长度: {}", responseBuffer.length());
		} catch (Exception e) {
			log.error("序列化 RpcResponse 失败", e);
			sendErrorResponse(connection, "Failed to serialize RpcResponse");
		}
	}

	/**
	 * 认证客户端
	 * @param request RPC请求
	 * @return 是否认证成功
	 */
	private boolean authenticateClient(RpcRequest request) {
		if (request.getCredentials() == null) {
			log.warn("请求中缺少客户端凭证");
			return false;
		}

		try {
			Authenticator authenticator = AuthenticatorFactory.getInstance(rpcConfig.getAuthenticator());
			return authenticator.authenticate(request.getCredentials());
		} catch (Exception e) {
			log.error("认证过程中发生异常", e);
			return false;
		}
	}

	/**
	 * 发送响应数据的方法 该方法负责将响应数据发送给客户端
	 *
	 * @param connection    服务器连接对象，用于发送数据
	 * @param responseBuffer 包含响应数据的缓冲区
	 */
	protected abstract void sendResponse(ServerConnection connection, Buffer responseBuffer);

	/**
	 * 发送错误响应的方法 该方法用于向客户端发送错误信息
	 *
	 * @param connection  服务器连接对象，用于发送数据
	 * @param message 错误消息
	 */
	protected abstract void sendErrorResponse(ServerConnection connection, String message);

}